#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/mman.h>

#define MEM_DEV_PATH "/dev/mem"
#define GPIO_MUX_BASE 0x200F0000

static const unsigned int gpio_mux_offset[][8] = 
{/// 0 is not used gpio now.
	{0},
	{0},
	{0},
	{0},
	{0},
	{0},
	{0x0080,0,0,0,0,0,0,0},
};

#define GPIO_DATA_OFFSET	0x03FC
#define GPIO_DIR_OFFSET		0x0400
static const unsigned int gpio_reg_base[] = 
{
	0x20140000,0x20150000,0x20160000,
	0x20170000,0x20180000,0x20190000,
	0x201a0000,0x201b0000,0x201c0000,
	0x201d0000,0x201e0000,0x201f0000
};

int read_gpio_value(int group, int pin)
{
	int fd, val = 0;
	void *p = NULL;

	if(group<0 || group>12 || pin<0 || pin>7 
			|| gpio_mux_offset[group][pin]==0)
	{
		printf("invalid gpio[%d_%d] pin!\n",group,pin);
		return -1;
	}
	fd = open(MEM_DEV_PATH, O_RDWR);
	if (fd < 0) 
	{
		printf("open %s err\n", MEM_DEV_PATH);
		return -1;
	}

	/** set gpio muxing */
	p = mmap((void *)0, 4*1024, PROT_WRITE, MAP_SHARED, fd, GPIO_MUX_BASE);
	if(p==MAP_FAILED)
	{
		printf("mmap gpio base mux reg fail:%s\n",strerror(errno));
		return -1;
	}
	(*(int *)(p+gpio_mux_offset[group][pin])) = 0; // gpio6_0 0
	munmap(p, 4*1024);

	usleep(30000);

	/** set gpio direction to input */
	p = mmap((void *)0, 4*1024, PROT_WRITE, MAP_SHARED, fd, gpio_reg_base[group]);
	if(p==MAP_FAILED)
	{
		printf("mmap gpio[%d] base reg fail:%s\n",group,strerror(errno));
		return -1;
	}
	(*(char *)(p+GPIO_DIR_OFFSET)) &= ~(1<<pin); // 0 - input,1 - output

	usleep(30000);
	/** read gpio data */
	val = ((*(char *)(p+GPIO_DATA_OFFSET))>>pin)&(1<<0);
	munmap(p, 4*1024);

	close(fd);

	return val;
}

int write_gpio_value(int group, int pin, int value)
{
	int fd, val = 0;
	void *p = NULL;

	if(group<0 || group>12 || pin<0 || pin>7 
			|| gpio_mux_offset[group][pin]==0)
	{
		printf("invalid gpio[%d_%d] pin!\n",group,pin);
		return -1;
	}
	fd = open(MEM_DEV_PATH, O_RDWR);
	if (fd < 0) 
	{
		printf("open %s err\n", MEM_DEV_PATH);
		return -1;
	}

	/** set gpio muxing */
	p = mmap((void *)0, 4*1024, PROT_WRITE, MAP_SHARED, fd, GPIO_MUX_BASE);
	if(p==MAP_FAILED)
	{
		printf("mmap gpio base mux reg fail:%s\n",strerror(errno));
		return -1;
	}
	(*(int *)(p+gpio_mux_offset[group][pin])) = 1; 
	munmap(p, 4*1024);

	usleep(30000);

	/** set gpio direction to output */
	p = mmap((void *)0, 4*1024, PROT_WRITE, MAP_SHARED, fd, gpio_reg_base[group]);
	if(p==MAP_FAILED)
	{
		printf("mmap gpio[%d] base reg fail:%s\n",group,strerror(errno));
		return -1;
	}
	(*(char *)(p+GPIO_DIR_OFFSET)) |= (1<<pin); // 0 - input,1 - output

	usleep(30000);
	/** write gpio data */
	val = !!value; 
	if(val)
		(*(char *)(p+GPIO_DATA_OFFSET)) |= (1<<pin);
	else
		(*(char *)(p+GPIO_DATA_OFFSET)) &= ~(1<<pin);
	munmap(p, 4*1024);
	close(fd);

	return 0;
}


